package net.sk.china.city.service.impl;

import cn.hutool.core.util.IdUtil;
import com.baomidou.mybatisplus.extension.conditions.query.QueryChainWrapper;
import com.github.pagehelper.Page;
import net.sk.china.city.domain.dto.TableDataInfo;
import net.sk.china.common.exception.ServiceException;
import net.sk.china.city.utils.PlatformIdHolder;
import lombok.RequiredArgsConstructor;
import net.sk.china.city.constant.ImportMode;
import net.sk.china.city.domain.ArRegion;
import net.sk.china.city.domain.ArTeam;
import net.sk.china.city.domain.User;
import net.sk.china.city.domain.dto.excel.AddUser;
import net.sk.china.city.domain.dto.user.*;
import net.sk.china.city.mapper.ArRegionMapper;
import net.sk.china.city.mapper.ArTeamMapper;
import net.sk.china.city.mapper.UserMapper;
import net.sk.china.city.observer.event.volunteer.VolunteerDecrEvent;
import net.sk.china.city.observer.event.volunteer.VolunteerIncrEvent;
import net.sk.china.city.service.IUserService;
import net.sk.china.city.utils.ConvertUtil;
import net.sk.china.city.utils.ExcelUtil;
import net.sk.china.city.utils.RedisCache;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;

/**
 * 前台用户Service业务层处理
 *
 * @author yangguanjin
 * @date 2024-04-07
 */
@RequiredArgsConstructor
@Service("cityUserService")
public class UserServiceImpl implements IUserService {
    private final UserMapper userMapper;
    private final ArTeamMapper arTeamMapper;
    private final ArRegionMapper arRegionMapper;
    private final RedisCache redisCache;
    private final ApplicationEventPublisher eventPublisher;

    /**
     * 查询前台用户
     *
     * @param userPkId 前台用户主键
     * @return 前台用户
     */
    @Override
    public UserDetailDTO selectUserByPkId(Long userPkId) {
        User user = userMapper.selectUserByPkId(userPkId);
        UserDetailDTO userDetailDTO = UserDetailDTO.from(user);

        // 加入的队伍
        List<TeamPairDTO> teamPairDTOList = new ArrayList<>();
        List<ArTeam> teamList = arTeamMapper.selectTeamListByUserId(userPkId);
        for (ArTeam arTeam : teamList) {
            TeamPairDTO teamPairDTO = new TeamPairDTO();
            teamPairDTO.setName(arTeam.getTeamName());
            teamPairDTO.setId(arTeam.getPkId());
            teamPairDTOList.add(teamPairDTO);
        }
        // 管理的队伍
        if (user.getRole() == 2) {
            Long managedTeamId = user.getManagedTeamId();
            ArTeam arTeam = arTeamMapper.selectArTeamByPkId(managedTeamId);
            TeamPairDTO teamPairDTO = new TeamPairDTO();
            teamPairDTO.setId(arTeam.getPkId());
            teamPairDTO.setName(arTeam.getTeamName());
            userDetailDTO.setManagedTeam(teamPairDTO);
        }

        userDetailDTO.setTeamIdNamePairList(teamPairDTOList);
        return userDetailDTO;
    }

    /**
     * 查询前台用户列表
     *
     * @param userSearchDTO 前台用户
     * @return 前台用户
     */
    @Override
    public TableDataInfo<UserItemDTO> selectUserList(UserSearchDTO userSearchDTO) {
        String platformId = PlatformIdHolder.getPlatformId();
        userSearchDTO.setPlatformId(platformId);
        Page<User> userPage = userMapper.selectUserList(userSearchDTO);
        List<UserItemDTO> userInfoDTOList = ConvertUtil.converList(userPage, UserItemDTO::from);
        return new TableDataInfo<>(userInfoDTOList, userPage.getTotal());
    }

    @Override
    public TableDataInfo<UserItemDTO> selectUserList2(UserSearchDTO userSearchDTO) {
        String platformId = PlatformIdHolder.getPlatformId();
        userSearchDTO.setPlatformId(platformId);
        Page<User> userList = userMapper.selectUserList2(userSearchDTO);
        List<UserItemDTO> userInfoDTOList = ConvertUtil.converList(userList, UserItemDTO::from);
        return new TableDataInfo<>(userInfoDTOList, userList.getTotal());
    }

    /**
     * 新增前台用户
     *
     * @param userUpdateDTO 前台用户
     * @return 结果
     */
    @Transactional
    @Override
    public int insertUser(UserUpdateDTO userUpdateDTO) {
        Optional<User> user = userMapper.selectUserByAccount(userUpdateDTO.getPhone());
        if (user.isPresent()) {
            throw new ServiceException("不能重复添加该用户（该手机号已占用）");
        }
        // 校验该用户是否能成为队长
        if (userUpdateDTO.getRole() == 2) {
            User teamLeader = userMapper.selectTeamLeader(userUpdateDTO.getManagedTeamId());
            if (teamLeader != null) {
                throw new ServiceException("该队已存在队长：" + teamLeader.getRealName());
            }
        } else {
            userUpdateDTO.setManagedTeamId(null);
        }
        userUpdateDTO.setPkId(IdUtil.getSnowflakeNextId());
        userUpdateDTO.setAccount(userUpdateDTO.getPhone());
        userUpdateDTO.setRegisterDatetime(LocalDateTime.now());
        userUpdateDTO.setJoinDatetime(LocalDateTime.now());
        // 为用户添加网格标识
        Long gridId = userUpdateDTO.getGridId();
        if (gridId != null && gridId != 0) {
            ArRegion region = arRegionMapper.selectArRegionByPkId(gridId);
            String regionIdList = region.getAncestors() + "," + region.getPkId();
            userUpdateDTO.setRegionIdList(regionIdList);
        }
        int insertCount = userMapper.insertUser(userUpdateDTO);
        // 将用户加入到多个队伍中
        List<Long> teamIdList = userUpdateDTO.getTeamIdList();
        if (teamIdList != null && !teamIdList.isEmpty()) {
            List<ArTeam> teamList = arTeamMapper.selectTeamList(teamIdList);
            arTeamMapper.addUserToTeamGroup(teamList, userUpdateDTO.getPkId());
        }
        // 维护region表中的volunteerCount
        eventPublisher.publishEvent(new VolunteerIncrEvent(this, userUpdateDTO.getRegionIdList()));
        return insertCount;
    }

    /**
     * 修改前台用户
     *
     * @param userUpdateDTO 前台用户
     * @return 结果
     */
    @Transactional
    @Override
    public int updateUser(UserUpdateDTO userUpdateDTO) {
        // 校验是否能成为队长
        if (userUpdateDTO.getRole() == 2) {
            User teamLeader = userMapper.selectTeamLeader(userUpdateDTO.getManagedTeamId());
            if (teamLeader != null && !Objects.equals(teamLeader.getPkId(), userUpdateDTO.getPkId())) {
                throw new ServiceException("该队已存在队长：" + teamLeader.getRealName());
            }
        } else {
            userUpdateDTO.setManagedTeamId(null);
        }
        // 换了网格，后续要加减对应的VolunteerCount
        boolean regionChanged = false;
        String newRegionIdList = null;
        String oldRegionIdList = null;
        // 更新网格；或者退出所有网格（null）
        if (userUpdateDTO.getGridId() != null && userUpdateDTO.getGridId() != 0) {
            ArRegion region = arRegionMapper.selectArRegionByPkId(userUpdateDTO.getGridId());
            newRegionIdList = region.getAncestors() + "," + region.getPkId();
            userUpdateDTO.setRegionIdList(newRegionIdList);
            User user = userMapper.selectUserByPkId(userUpdateDTO.getPkId());
            oldRegionIdList = user.getRegionIdList();
            if (!Objects.equals(oldRegionIdList, newRegionIdList)) {
                regionChanged = true;
            }
        } else {
            User user = userMapper.selectUserByPkId(userUpdateDTO.getPkId());
            oldRegionIdList = user.getRegionIdList();
            regionChanged = true;
        }

        int updateCount = userMapper.updateUser(userUpdateDTO);
        // 更新队伍：删除旧队伍，加入新队伍；或者直接退出所有队伍（null）
        List<Long> teamIdList = userUpdateDTO.getTeamIdList();
        if (teamIdList != null && !teamIdList.isEmpty()) {
            arTeamMapper.removeUserFromTeamGroup(userUpdateDTO.getPkId());
            List<ArTeam> teamList = arTeamMapper.selectTeamList(teamIdList);
            arTeamMapper.addUserToTeamGroup(teamList, userUpdateDTO.getPkId());
        } else {
            arTeamMapper.removeUserFromTeamGroup(userUpdateDTO.getPkId());
        }
        // 更新volunteerCount
        if (regionChanged) {
            eventPublisher.publishEvent(new VolunteerDecrEvent(this, oldRegionIdList));
            eventPublisher.publishEvent(new VolunteerIncrEvent(this, newRegionIdList));
        }
        redisCache.deleteObject("login:info:" + userUpdateDTO.getPkId());
        return updateCount;
    }

    /**
     * 修改积分
     *
     * @param userUpdateDTO
     */
    @Override
    public int updatePoint(UserUpdateDTO userUpdateDTO) {
        return userMapper.updatePoint(userUpdateDTO);
    }

    /**
     * 批量删除前台用户
     *
     * @param pkIds 需要删除的前台用户主键
     * @return 结果
     */
    @Override
    public int deleteUserByPkIds(Long[] pkIds) {
        List<User> userList = userMapper.selectUserListByPkIdList(pkIds);
        int updateCount = userMapper.deleteUserByPkIds(pkIds);
        for (User user : userList) {
            eventPublisher.publishEvent(new VolunteerDecrEvent(this, user.getRegionIdList()));
        }
        return updateCount;
    }

    /**
     * 删除前台用户信息
     *
     * @param pkId 前台用户主键
     * @return 结果
     */
    @Override
    public int deleteUserByPkId(Long pkId) {
        User user = userMapper.selectUserByPkId(pkId);
        int updateCount = userMapper.deleteUserByPkId(pkId);
        eventPublisher.publishEvent(new VolunteerDecrEvent(this, user.getRegionIdList()));
        return updateCount;
    }

    @Override
    public int importUser(MultipartFile multipartFile) {
        try {
            ExcelUtil.importUser(multipartFile, ImportMode.INCR);
        } catch (IOException e) {
            throw new ServiceException("志愿者导入失败");
        }
        return 1;
    }

    @Override
    public int fullImportUser(MultipartFile multipartFile) {
        try {
            ExcelUtil.importUser(multipartFile, ImportMode.FULL);
        } catch (IOException e) {
            throw new ServiceException("志愿者导入失败");
        }
        return 1;
    }

    /**
     * 根据手机号查询用户积分信息
     *
     * @param phone
     * @return
     */
    @Override
    public User getUserByPhone(String phone) {
        return userMapper.getUserByPhone(phone);
    }

    private final Object lockObj1 = new Object();
    @Transactional(rollbackFor = Exception.class)
    @Override
    public int insertUserBatch(List<AddUser> addUserList) {
        synchronized (lockObj1) {
            for (AddUser addUser : addUserList) {
                ArRegion arRegion = regionCheck(addUser.getRegionCode());
                teamCheck(addUser);
                String account = addUser.getAccount();
                Optional<User> userOpt = new QueryChainWrapper<>(userMapper).eq("account", account)
                        .eq("is_delete", 0)
                        .oneOpt();
                if (userOpt.isPresent()) {
                    // 存在的用户数据，跳过处理
                    continue;
                }
                User user = rebuildUserFromAddUser(addUser, arRegion);
                userMapper.insert(user);
                incrVolunteerCount(arRegion);
                addUserToTeam(addUser, user);
            }
            return 1;
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public int fullInsertUserBatch(List<AddUser> addUserList) {
        synchronized (lockObj1) {
            for (AddUser addUser : addUserList) {
                ArRegion arRegion = regionCheck(addUser.getRegionCode());
                teamCheck(addUser);
                String account = addUser.getAccount();
                Optional<User> userOpt = new QueryChainWrapper<>(userMapper).eq("account", account)
                        .eq("is_delete", 0)
                        .oneOpt();
                if (userOpt.isPresent()) {
                    // 存在则修改用户信息，而不是跳过
                    User user = rebuildUserFromAddUser(addUser, arRegion);
                    user.setPkId(userOpt.get().getPkId());
                    userMapper.updateById(user);
                    addUserToTeam(addUser, user);
                    continue;
                }
                User user = rebuildUserFromAddUser(addUser, arRegion);
                userMapper.insert(user);
                incrVolunteerCount(arRegion);
                addUserToTeam(addUser, user);
            }
            return 1;
        }
    }


    private ArRegion regionCheck(String regionCode) {
        ArRegion arRegion = null;
        if (regionCode != null) {
            arRegion = arRegionMapper.selectArRegionByCode(regionCode);
            if (arRegion == null) {
                throw new ServiceException("网格：" + regionCode + ",尚未添加进系统");
            }
        }
        return arRegion;
    }

    private void teamCheck(AddUser addUser) {
        if (StringUtils.isNotBlank(addUser.getTeamName())) {
            if (addUser.getTeamId() == null) {
                String teamName = addUser.getTeamName();
                throw new ServiceException("队伍：" + teamName + ",尚未添加进系统");
            }
        }
    }

    private User rebuildUserFromAddUser(AddUser addUser, ArRegion arRegion) {
        User user = new User();
        user.setAccount(addUser.getAccount());
        user.setIdNumber(addUser.getIdNumber());
        user.setRealName(addUser.getRealName());
        user.setNickName(addUser.getRealName());
        user.setSex(addUser.getSex());
        user.setWorkHours(addUser.getWorkHours());
        user.setPoint(addUser.getPoint());
        user.setBirthDate(addUser.getBirthDate());
        if (addUser.getRegionCode() != null) {
            user.setRegionIdList(arRegion.getAncestors() + "," + arRegion.getPkId());
        } else {
            user.setRegionIdList("");
        }
        // todo 应该从platformId中截取，而不是写死
        user.setPlatformId("0,100");
        if (addUser.getTeamName() == null && addUser.getRegionCode() == null) {
            user.setRole(0);
        } else {
            user.setRole(1);
        }
        user.setJoinDatetime(LocalDateTime.now());
        user.setCreateDatetime(LocalDateTime.now());
        user.setUpdateDatetime(LocalDateTime.now());
        return user;
    }

    private void incrVolunteerCount(ArRegion arRegion) {
        if (arRegion != null) {
            String fullRegionId = arRegion.getAncestors() + "," + arRegion.getPkId();
            eventPublisher.publishEvent(new VolunteerDecrEvent(this, fullRegionId));
        }
    }

    private void addUserToTeam(AddUser addUser, User user) {
        // 清空用户参与的队伍（对于增量导入，没有队伍；对于全量导入，有原有队伍，也是删除掉再加入）
        Long userPkId = user.getPkId();
        arTeamMapper.removeUserFromTeamGroup(userPkId);
        if (addUser.getTeamName() != null) {
            ArTeam arTeam = arTeamMapper.selectArTeamByPkId(addUser.getTeamId());
            String platformId = arTeam.getPlatformId();
            arTeamMapper.addUserToTeam(addUser.getTeamId(), userPkId, platformId);
        }
    }
}
